import javax.microedition.lcdui.Canvas;
import javax.microedition.lcdui.Command;
import javax.microedition.lcdui.CommandListener;
import javax.microedition.lcdui.Display;
import javax.microedition.lcdui.Displayable;
import javax.microedition.lcdui.Font;
import javax.microedition.lcdui.Graphics;


public class UnicodeCanvas extends Canvas implements CommandListener
{
	char topLeftCharacter = 0;
	char selectedCharacter = 0;
	int currentRange = 0;
	int cols;
	int rows;
	UnicodeMidlet midlet;

	String[] rangeNames = new String[]
 	{
 		"Basic Latin",
 		"Latin-1 Supplement",
 		"Latin Extended-A",
 		"Latin Extended-B",
 		"IPA Extensions",
 		"Spacing Modifier Letters",
 		"Combining Diacritical Marks",
 		"Greek and Coptic",
 		"Cyrillic",
 		"Cyrillic Supplement",
 		"Armenian",
 		"Hebrew",
 		"Arabic",
 		"Syriac",
 		"Thaana",
 		"Devanagari",
 		"Bengali",
 		"Gurmukhi",
 		"Gujarati",
 		"Oriya",
 		"Tamil",
 		"Telugu",
 		"Kannada",
 		"Malayalam",
 		"Sinhala",
 		"Thai",
 		"Lao",
 		"Tibetan",
 		"Myanmar",
 		"Georgian",
 		"Hangul Jamo",
 		"Ethiopic",
 		"Cherokee",
 		"Canadian Aboriginal Syllabics",
 		"Ogham",
 		"Runic",
 		"Tagalog",
 		"Hanunoo",
 		"Buhid",
 		"Tagbanwa",
 		"Khmer",
 		"Mongolian",
 		"Limbu",
 		"Tai Le",
 		"Khmer Symbols",
 		"Phonetic Extensions",
 		"Latin Extended Additional",
 		"Greek Extended",
 		"General Punctuation",
 		"Superscripts and Subscripts",
 		"Currency Symbols",
 		"Combining Marks for Symbols",
 		"Letterlike Symbols",
 		"Number Forms",
 		"Arrows",
 		"Mathematical Operators",
 		"Miscellaneous Technical",
 		"Control Pictures",
 		"Optical Character Recognition",
 		"Enclosed Alphanumerics",
 		"Box Drawing",
 		"Block Elements",
 		"Geometric Shapes",
 		"Miscellaneous Symbols",
 		"Dingbats",
 		"Miscellaneous Math Symbols-A",
 		"Supplemental Arrows-A",
 		"Braille Patterns",
 		"Supplemental Arrows-B",
 		"Miscellaneous Math Symbols-B",
 		"Supplemental Math Operators",
 		"Misc. Symbols and Arrows",
 		"CJK Radicals Supplement",
 		"Kangxi Radicals",
 		"Ideographic Desc. Characters",
 		"CJK Symbols and Punctuation",
 		"Hiragana",
 		"Katakana",
 		"Bopomofo",
 		"Hangul Compatibility Jamp",
 		"Kanbun",
 		"Bopomofu Extended",
 		"Katakana Phonetic Extensions",
 		"Enclosed CJK Letters and Months",
 		"CJK Compatibility",
 		"CJK Unified Ideographs Ext. A",
 		"Yijing Hexagram Symbols",
 		"CJK Unified Ideographs",
 		"Yi Syllables",
 		"Yi Radicals",
 		"Hungal Syllables",
 		"Private Use Area",
 		"CJK Compatibility Ideographs",
 		"Alphabetic Presentation Forms",
 		"Arabic Presentation Forms-A",
 		"Variation Selectors",
 		"Combining Half Marks",
 		"CJK Compatibility Forms",
 		"Small Form Variant",
 		"Arabic Presentation Forms-B",
 		"Halfwidth and Fullwidth Forms",
 		"Specials",
 	};

 	char[] offsets = new char[]
 	{
 		0x0, 	//		"Basic Latin",
 		0x80,	//		"Latin-1 Supplement",
 		0x100,	//		"Latin Extended-A",
 		0x180,	//		"Latin Extended-B",
 		0x250,	//		"IPA Extensions",
 		0x2B0,	//		"Spacing Modifier Letters",
 		0x300,	//		"Combining Diacritical Marks",
 		0x370,	//		"Greek and Coptic",
 		0x400,	//		"Cyrillic",
 		0x500,	//		"Cyrillic Supplement",
 		0x530,	//		"Armenian",
 		0x590,	//		"Hebrew",
 		0x600,	//		"Arabic",
 		0x700,	//		"Syriac",
 		0x780,	//		"Thaana",
 		0x900,	//		"Devanagari",
 		0x980,	//		"Bengali",
 		0xA00,	//		"Gurmukhi",
 		0xA80,	//		"Gujarati",
 		0xB00,	//		"Oriya",
 		0xB80,	//		"Tamil",
 		0xC00,	//		"Telugu",
 		0xC80,	//		"Kannada",
 		0xD00,	//		"Malayalam",
 		0xD80,	//		"Sinhala",
 		0xE00,	//		"Thai",
 		0xE80,	//		"Lao",
 		0xF00,	//		"Tibetan",
 		0x1000,	//		"Myanmar",
 		0x10A0,	//		"Georgian",
 		0x1100,	//		"Hangul Jamo",
 		0x1200,	//		"Ethiopic",
 		0x13A0,	//		"Cherokee",
 		0x1400,	//		"Canadian Aboriginal Syllabics",
 		0x1680,	//		"Ogham",
 		0x16A0,	//		"Runic",
 		0x1700,	//		"Tagalog",
 		0x1720,	//		"Hanunoo",
 		0x1740,	//		"Buhid",
 		0x1760,	//		"Tagbanwa",
 		0x1780,	//		"Khmer",
 		0x1800,	//		"Mongolian",
 		0x1900,	//		"Limbu",
 		0x1950,	//		"Tai Le",
 		0x19E0,	//		"Khmer Symbols",
 		0x1D00,	//		"Phonetic Extensions",
 		0x1E00,	//		"Latin Extended Additional",
 		0x1F00,	//		"Greek Extended",
 		0x2000,	//		"General Punctuation",
 		0x2070,	//		"Superscripts and Subscripts",
 		0x20A0,	//		"Currency Symbols",
 		0x20D0,	//		"Combining Marks for Symbols",
 		0x2100,	//		"Letterlike Symbols",
 		0x2150,	//		"Number Forms",
 		0x2190,	//		"Arrows",
 		0x2200,	//		"Mathematical Operators",
 		0x2300,	//		"Miscellaneous Technical",
 		0x2400,	//		"Control Pictures",
 		0x2440,	//		"Optical Character Recognition",
 		0x2460,	//		"Enclosed Alphanumerics",
 		0x2500,	//		"Box Drawing",
 		0x2580,	//		"Block Elements",
 		0x25A0,	//		"Geometric Shapes",
 		0x2600,	//		"Miscellaneous Symbols",
 		0x2700,	//		"Dingbats",
 		0x27C0,	//		"Miscellaneous Math Symbols-A",
 		0x27F0,	//		"Supplemental Arrows-A",
 		0x2800,	//		"Braille Patterns",
 		0x2900,	//		"Supplemental Arrows-B",
 		0x2980,	//		"Miscellaneous Math Symbols-B",
 		0x2A00,	//		"Supplemental Math Operators",
 		0x2B00,	//		"Misc. Symbols and Arrows",
 		0x2E80,	//		"CJK Radicals Supplement",
 		0x2F00,	//		"Kangxi Radicals",
 		0x2FF0,	//		"Ideographic Desc. Characters",
 		0x3000,	//		"CJK Symbols and Punctuation",
 		0x3040,	//		"Hiragana",
 		0x30A0,	//		"Katakana",
 		0x3100,	//		"Bopomofo",
 		0x3130,	//		"Hangul Compatibility Jamp",
 		0x3190,	//		"Kanbun",
 		0x31A0,	//		"Bopomofu Extended",
 		0x31F0,	//		"Katakana Phonetic Extensions",
 		0x3200,	//		"Enclosed CJK Letters and Months",
 		0x3300,	//		"CJK Compatibility",
 		0x3400,	//		"CJK Unified Ideographs Ext. A",
 		0x4DC0,	//		"Yijing Hexagram Symbols",
 		0x4E00,	//		"CJK Unified Ideographs",
 		0xA000,	//		"Yi Syllables",
 		0xA490,	//		"Yi Radicals",
 		0xAC00,	//		"Hungal Syllables",
 		0xE000,	//		"Private Use Area",
 		0xF900,	//		"CJK Compatibility Ideographs",
 		0xFB00,	//		"Alphabetic Presentation Forms",
 		0xFB50,	//		"Arabic Presentation Forms-A",
 		0xFE00,	//		"Variation Selectors",
 		0xFE20,	//		"Combining Half Marks",
 		0xFE30,	//		"CJK Compatibility Forms",
 		0xFE50,	//		"Small Form Variant",
 		0xFE70,	//		"Arabic Presentation Forms-B",
 		0xFF00,	//		"Halfwidth and Fullwidth Forms",
 		0xFFF0,	//		"Specials",
 	};

 	int[] rangeColours;


	public UnicodeCanvas(UnicodeMidlet midlet)
	{
		this.midlet = midlet;

		rangeColours = new int[offsets.length];

		for (int i = 0; i < rangeColours.length; i++)
		{
			rangeColours[i] = i % 2 == 0 ? 0xFFEEEEEE : 0xFFCCCCCC;
		}

		// Setup menus
		addCommand(new Command("Go", "Select Range", Command.SCREEN, 0));
		addCommand(new Command("Exit", Command.EXIT, 0));
		setCommandListener(this);
	}

	public void keyRepeated(int keyCode)
	{
		keyPressed(keyCode);
	}

	public void keyPressed(int keyCode)
	{
		int action = getGameAction(keyCode);

		if (cols != 0 && rows != 0)
		{
			if (action == UP || keyCode == KEY_NUM2)
			{
				if (selectedCharacter < cols)
				{
					selectedCharacter -= cols - (65535 - 65535 % cols);
				}
				else
				{
					selectedCharacter -= cols;
				}
			}
			else if (action == DOWN || keyCode == KEY_NUM8)
			{
				if (selectedCharacter > 65535 - cols)
				{
					selectedCharacter += cols + (65535 % cols) + 1;
				}
				else
				{
					selectedCharacter += cols;
				}
			}
			else if (action == LEFT || keyCode == KEY_NUM4)
			{
				selectedCharacter--;
			}
			else if (action == RIGHT || keyCode == KEY_NUM6)
			{
				selectedCharacter++;
			}

			setSelectedCharacter(selectedCharacter);
		}
	}

	public void setSelectedCharacter(char newSelectedChar)
	{
		this.selectedCharacter = newSelectedChar;
		topLeftCharacter = (char) (selectedCharacter - selectedCharacter % (cols * rows));

		currentRange = getRange(selectedCharacter);

		repaint();
	}

	public int getRange(char character)
	{
		// Find the range the character is in
		int selectedRange = -1;
		int range = 0;
		while (range < offsets.length && selectedRange < 0)
		{
			if (range < offsets.length - 1)
			{
				if (character >= offsets[range] && character < offsets[range + 1])
				{
					selectedRange = range;
				}
			}
			else
			{
				if (character >= offsets[range])
				{
					selectedRange = range;
				}
			}

			range++;
		}

		return selectedRange;
	}

	protected void paint(Graphics g)
	{
		g.setColor(0xFFFFFFFF);
		g.fillRect(0, 0, getWidth(), getHeight());

		Font font = g.getFont();

		// Max width of the font (roughly)
		int maxWidth = 2 * font.charWidth('W');

		printGrid(g, maxWidth, font.getHeight());

	}

	private void printGrid(Graphics g, int charWidth, int charHeight)
	{
		Font font = g.getFont();

		int verticalBorder = charHeight;
		int border = 5;

		g.setColor(0xFF000000);

		g.drawString("hex: " + Integer.toHexString(selectedCharacter) +
				", dec: " + (int) selectedCharacter, border, 0,
				Graphics.TOP | Graphics.LEFT);

		g.drawString(rangeNames[currentRange], border, getHeight() - charHeight, Graphics.TOP | Graphics.LEFT);

		cols = (getWidth() - 2 * border) / charWidth;
		rows = (getHeight() - (2 * verticalBorder)) / charHeight;

		char currentChar = topLeftCharacter;
		int currentRange = getRange(currentChar);
		int backColour = rangeColours[currentRange];

		for (int j = 0; j < rows; j++)
		{
			for (int i = 0; i < cols; i++)
			{
				int x = border + i * charWidth;
				int y = verticalBorder + j * charHeight;

				if (currentChar == selectedCharacter)
				{
					g.setColor(0xFF7777FF);
				}
				else
				{
					g.setColor(backColour);
				}

				g.fillRect(x, y, charWidth, charHeight);

				g.setColor(0xFF000000);

				// Centre the character
				x += (charWidth - font.charWidth(currentChar)) / 2;

				g.drawChar(currentChar, x, y, Graphics.TOP | Graphics.LEFT);

				// Don't draw anything after the last character
				// (it would wrap but not properly)
				if (currentChar == 0xFFFF)
				{
					return;
				}

				currentChar++;

				if (currentRange < offsets.length - 1 &&
					currentChar >= offsets[currentRange + 1])
				{
					currentRange++;
					backColour = rangeColours[currentRange];
				}
			}
		}
	}

	public void commandAction(Command command, Displayable d)
	{
		if (command.getCommandType() == Command.EXIT)
		{
			midlet.notifyDestroyed();
		}
		else if (command.getCommandType() == Command.SCREEN)
		{
			SelectRangeForm form = new SelectRangeForm(this);

			Display.getDisplay(midlet).setCurrent(form);
		}
	}

}
